package CAS;

import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyData {  //MyData.java MyData.class  === JVM字节码

    volatile int number = 0; //volatile 增加属性的可见性 但是无法保证原子性
    AtomicInteger atomicInteger = new AtomicInteger();

    public void addT060() {
        this.number = 60;
    }

    public void addPlusPlus() {
        number++;
    }

    public void addAtomic() {
        // 获取后自增
        atomicInteger.getAndIncrement();
    }

    // 指令重排
    //正常 1234
    //指令重排后可能是2134
    //1324
    public void mysort() {
        int x = 11;
        int y = 12;
        x = x + 5;
        y = x * x;
    }


}

/***
 * 验证volatile的可见性
 * 1:假如 int number = 0 number 变量之前根本没有增加volatile关键字修饰 没有可见性
 * 1.2 添加了 volatile ，可以解决可见性问题
 * 2.验证volatile 不保证原子性  不可分割 完整性  也即某个线程正在做某个具体业务，中间不可被加塞或者被分割
 * 需要整体完整 要么同时成功 要么同时失败
 *
 */
public class VolatileDemo {

    /**
     * volatile只能保证可见性  不能保证原子性 禁止指令重排
     * 测试volatile 和 AtomicInteger
     */
    @Test
    public void volatileTest() {

        MyData myData = new MyData();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.addPlusPlus();
                    myData.addAtomic();
                }
            }, String.valueOf(i)).start();
        }

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "\t int finally number value:  " + myData.number);
        System.out.println(Thread.currentThread().getName() + "\t AtomicInteger finally number value:  " + myData.atomicInteger);

    }

    /***
     *  volatile可以保证可见性 及时通知其他线程  主物理内存的值 已经被修改
     *  可以去掉volatile看下效果
     */
    @Test
    public void seeOkByVolatile() {
        MyData myData = new MyData();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t come in value \t" + myData.number);
            try {
                TimeUnit.SECONDS.sleep(3);
                myData.addT060();
                System.out.println(Thread.currentThread().getName() + "\t update number value" + myData.number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "AAA").start();

        while (myData.number == 0) {

        }
        System.out.println(Thread.currentThread().getName() + "\t mission is over value \t   " + myData.number);
    }
}
